﻿using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Markup;
using System.Xaml;

namespace Geometry
{
    [RuntimeNameProperty("Name")]
    public abstract class GObjectBase : IGObject
    {
        protected GObjectBase()
        {
            this.DependencyCollection = new ObservableCollection<IGObject>();
            this.Dependencies = new ReadOnlyObservableCollection<IGObject>(this.DependencyCollection);

            this.DependentCollection = new ObservableCollection<IGObject>();
            this.Dependents = new ReadOnlyObservableCollection<IGObject>(this.DependentCollection);

            this.PropertyChangedEventMutex = new object();
            NameDisplay = new GNameDisplay();
        }

        public ReadOnlyObservableCollection<IGObject> Dependencies { get; private set; }
        public ReadOnlyObservableCollection<IGObject> Dependents { get; private set; }

        protected ObservableCollection<IGObject> DependencyCollection { get; private set; }
        protected ObservableCollection<IGObject> DependentCollection { get; private set; }

        private PropertyChangedEventHandler PropertyChangedEvent;
        private readonly object PropertyChangedEventMutex;

        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                lock (this.PropertyChangedEventMutex)
                {
                    var dependent = value.Target as GObjectBase;
                    if (dependent != null)
                    {
                        this.DependentCollection.Add(dependent);
                        dependent.DependencyCollection.Add(this);
                    }
                    this.PropertyChangedEvent += value;
                }
            }

            remove
            {
                lock (this.PropertyChangedEventMutex)
                {
                    var dependent = value.Target as GObjectBase;
                    if (dependent != null)
                    {
                        this.DependentCollection.Remove(dependent);
                        dependent.DependencyCollection.Remove(this);
                    }
                    this.PropertyChangedEvent -= value;
                }
            }
        }

        protected void RaisePropertyChanged(string _propertyName)
        {
            if(m_disposing) return;

            PropertyChangedEventHandler handlers = null;
            lock (this.PropertyChangedEventMutex)
            {
                handlers = this.PropertyChangedEvent;
            }
            if (handlers == null) return;

            handlers(this, new PropertyChangedEventArgs(_propertyName));
        }

        private bool m_IsEnabled = true;
        [DefaultValue(true)]
        public bool IsEnabled
        {
            get { return m_IsEnabled; }
            set
            {
                if (value.Equals(m_IsEnabled)) return;
                m_IsEnabled = value;
                RaisePropertyChanged("IsEnabled");
            }
        }

        public string Name { get; set; }

        public GNameDisplay NameDisplay { get; set; }

        public Rect? BoundingBox { get; protected set; }

        protected abstract void UpdateBoundingBox();

        protected abstract void ReleaseDependencies();

        private bool m_disposing = false;

        public void Dispose()
        {
            m_disposing = true;
            ReleaseDependencies();

        }
    }

    public class GNameDisplay : IDraggable, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string _propertyName)
        {
            var temp = PropertyChanged;
            if (temp != null)
            {
                temp(this, new PropertyChangedEventArgs(_propertyName));
            }
        }

        private bool m_Visibility = true;
        [DefaultValue(true)]
        public bool Visibility
        {
            get { return m_Visibility; }
            set
            {
                if (value.Equals(m_Visibility)) return;
                m_Visibility = value;
                RaisePropertyChanged("Visibility");
            }
        }   
        
        private Vector m_OffSet = new Vector(10, 10);
        [DefaultValue(typeof(Vector), "10, 10")]
        public Vector Offset
        {
            get { return m_OffSet; }
            set
            {
                if (value.Equals(m_OffSet)) return;
                m_OffSet = value;
                RaisePropertyChanged("Offset");
            }
        }

        #region IDraggable Members

        private Vector m_BeginOffset;
        private bool m_IsDragged = false;

        public void BeginDrag()
        {
            m_BeginOffset = Offset;
        }

        public void CancelDrag()
        {
            Offset = m_BeginOffset;
        }

        public void EndDrag()
        {
            m_IsDragged = false;
        }

        public void DragBy(Vector amount)
        {
            m_IsDragged = true;
            Offset += amount;
        }

        public bool IsDragged
        {
            get { return m_IsDragged; }
        }

        #endregion
    }
}
